﻿/*
 * Copyright (C) 2023 - 2024 lchfj.cn
 * 
 * This file is part of FastestHost.
 * 
 * FastestHost is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * FastestHost is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with FastestHost. If not, see <https://www.gnu.org/licenses/>.
 */


using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Cn.Lchfj.FastestHost
{
    [JsonSerializable(typeof(List<HostIPTestResult>))]
    [JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
    internal partial class LocalHistoryJsonContext : JsonSerializerContext
    {

    }
    internal class LocalCheckHistory
    {
        private static readonly string StoreDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "FastestHost");

        internal static Task DeleteHistoryAsync()
        {
            Directory.Delete(StoreDirectory, true);
            return Task.CompletedTask;
        }
        internal static async Task<IReadOnlyList<HostIP>> CombineAsync(IReadOnlyList<HostIPTestResult> testResultList)
        {
            Directory.CreateDirectory(StoreDirectory);

            if (testResultList is not { Count: > 0 })
                return [];


            var history = await ReadHistoryAsync();
            history = ChangeHistoryIP(history, testResultList);
            history.AddRange(testResultList.Where(r => r.Time > 0));

            await SaveNewList(testResultList);


            return GetFastList(history);
        }

        private static List<HostIP> GetFastList(List<HostIPTestResult> history)
        {
            var list = new List<HostIP>();

            var groupList = (from item in history
                             where item.Time > 0
                             group item by item.Host into gro
                             select gro).ToList();

            foreach (var gro in groupList)
            {
                var item = (from ip in gro
                            orderby ip.Time ascending
                            group ip by ip.IP into gr
                            orderby gr.Count() descending, gr.Average(i => i.Time) ascending
                            select gr.First()).ToList();

                list.Add(item.First().ToHostIP());
            }

            return list;
        }

        private static List<HostIPTestResult> ChangeHistoryIP(IEnumerable<HostIPTestResult> history, IReadOnlyList<HostIPTestResult> testResultList)
        {
            var list = new List<HostIPTestResult>();

            var ipList = testResultList.Select(i => i.IP).Distinct().ToList();
            foreach (var ip in ipList)
            {
                var hashCode = HashIP(ip);

                var sub = history.Where(h => h.IP == hashCode).Select(h => h with { IP = ip });

                list.AddRange(sub);
            }

            return list;
        }

        private static async Task SaveNewList(IReadOnlyList<HostIPTestResult> testResultList)
        {
            var saveData = testResultList.Where(i => i.Time > 0).Select(i => i with { IP = HashIP(i.IP) }).ToList();
            if (saveData is not { Count: > 0 })
                return;

            var lastTime = DateTime.UtcNow.Ticks;
            var path = Path.Combine(StoreDirectory, lastTime + ".json");
            var json = JsonSerializer.Serialize(saveData, LocalHistoryJsonContext.Default.ListHostIPTestResult);
            await File.WriteAllTextAsync(path, json);
        }

        private static string HashIP(string iP)
        {
            var bytes = Encoding.ASCII.GetBytes(iP);
            var hash = SHA1.HashData(bytes);
            return Convert.ToHexString(hash).ToLower();
        }

        private static async Task<List<HostIPTestResult>> ReadHistoryAsync()
        {
            var result = new List<HostIPTestResult>();

            var fileList = Directory.GetFiles(StoreDirectory, "*.json").OrderDescending();

            var count = 0;
            foreach (var filePath in fileList)
            {
                var name = Path.GetFileNameWithoutExtension(filePath);
                if (!long.TryParse(name, out var ticks))
                    continue;

                count++;
                if (count > 10)
                {
                    File.Delete(filePath);
                    continue;
                }


                var json = await File.ReadAllTextAsync(filePath);
                if (string.IsNullOrWhiteSpace(json))
                {
                    File.Delete(filePath);
                    continue;
                }


                List<HostIPTestResult>? jList;
                try
                {
                    jList = JsonSerializer.Deserialize(json, LocalHistoryJsonContext.Default.ListHostIPTestResult);
                }
                catch
                {
                    File.Delete(filePath);
                    continue;
                }


                result.AddRange(jList ?? []);
            }

            return result;
        }
    }
}